﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Text;
using T.FAS.Runtime.Metadata.Dao;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Metadata.Service;
using T.FAS.Runtime.Metadata.SPI;
using T.ServiceComponent.Error;

namespace T.FAS.Runtime.Metadata.Client
{
    class MetaDataCacheCoordinator
    {
        private IMetaDataCache firstCache;
        private IMetaDataCache secondCache;
        private string TenantID;
        private static object syncObj = new object();

        private static MetaDataCacheCoordinator cache;

        public static MetaDataCacheCoordinator GetSingleton()
        {
            if (cache == null)
            {
                lock (syncObj)
                {
                    if (cache == null)
                    {
                        cache = new MetaDataCacheCoordinator();
                    }
                }
            }

            return cache;
        }

        private MetaDataCacheCoordinator()
        {
            firstCache = MetaDataCacheFactory.GetCache(MetaDataCacheLevel.FirstLevel);
            secondCache = MetaDataCacheFactory.GetCache(MetaDataCacheLevel.SecondLevel);
            TenantID = UserUtil.GetCurrentUserTenantId();
        }

        /// <summary>
        /// 获取元数据
        /// </summary>
        /// <param name="metaDataID">元数据ID</param>
        /// <returns>元数据</returns>      
        public string GetMetaDataWithNoDeSerialize(string metaDataID)
        {
            try
            {
                var metadata = new MeteDataService().GetMetaData(metaDataID);
                if (metadata == null)
                    throw new TException("FAS-Meta-1002", $"从Redis或DB中获取元数据({metaDataID})失败！");
                return new MetaDataContentManager().SerializeMetaData(metadata);
            }
            catch (Exception e)
            {
                throw new TException("FAS-Meta-1002", $"获取元数据({metaDataID})失败！", e);
            }
        }
        /// <summary>
        /// 获取元数据
        /// </summary>
        /// <param name="metaDataID">元数据ID</param>
        /// <returns>元数据</returns>      
        public IMetaData GetMetaData(string metaDataID)
        {
            try
            {
                //LogUtil.Log($"Start GetMetaData:{metaDataID}");
                var isFallback = false;
                var firstMetaData = firstCache.Get(metaDataID);
                if (firstMetaData == null)
                {
                    var secondMetaData = GetSyncSecondMetaData(metaDataID, out isFallback);
                    if (secondMetaData == null)
                        throw new TException("FAS-Meta-1002", $"从Redis或DB中获取元数据({metaDataID})失败！");

                    firstMetaData = GetSyncFirstMetaData(metaDataID, firstMetaData, secondMetaData);
                }
                else
                {
                    if (!firstCache.IsCacheMetaDataValid(metaDataID))
                    {
                        if (IsCacheChanged(metaDataID))
                        {
                            var secondModel = GetSyncSecondMetaData(metaDataID, out isFallback);
                            if (secondModel != null)
                            {
                                firstMetaData = GetSyncFirstMetaData(metaDataID, firstMetaData, secondModel);
                            }
                            else
                            {
                                if (isFallback == false)
                                {
                                    //Redis和DB中数据模型被删除了, 同步删除本地缓存
                                    firstMetaData = GetSyncFirstMetaData(metaDataID, firstMetaData, secondModel);
                                }
                            }
                        }
                    }
                }
                //LogUtil.Log($"End GetMetaData:{metaDataID}");
                //元数据扩展的重新组装
                return ReWraperMetadata(firstMetaData);
            }
            catch (Exception e)
            {
                throw new TException("FAS-Meta-1002", $"获取元数据({metaDataID})失败！{e}", e);
            }
        }
        /// <summary>
        /// 对元数据进行二次包装，更新关联的元数据信息
        /// </summary>
        /// <param name="metaData">元数据</param>
        /// <returns></returns>
        private IMetaData ReWraperMetadata(IMetaData metaData)
        {
            var metadataType = metaData.MetaTypeID;
            var extManager = MetaDataManagerFactory.CreateExtMetaDataManager(metadataType);
            if (extManager != null)
            {
                return extManager.GetMetaDataExt(metaData);
            }
            else
            {
                return metaData;
            }
        }

        public IMetaData GetMetaDataByCode(string metaDataCode, string metaTypeID)
        {
            try
            {
                //LogUtil.Log($"Start GetMetaDataByCode:{metaDataCode}");
                var isFallback = false;
                var firstMetaData = firstCache.Get(metaDataCode, metaTypeID);
                if (firstMetaData == null)
                {
                    var secondMetaData = GetSyncSecondMetaData(metaDataCode, metaTypeID, out isFallback);
                    if (secondMetaData == null)
                        throw new TException("FAS-Meta-1002", $"从Redis或DB中获取元数据({metaTypeID}:{metaDataCode}:{TenantID})失败！");

                    firstMetaData = GetSyncFirstMetaData(metaDataCode, metaTypeID, firstMetaData, secondMetaData);
                }
                else
                {
                    if (IsCacheChanged(metaDataCode))
                    {
                        var secondModel = GetSyncSecondMetaData(metaDataCode, metaTypeID, out isFallback);
                        if (secondModel != null)
                        {
                            firstMetaData = GetSyncFirstMetaData(metaDataCode, metaTypeID, firstMetaData, secondModel);
                        }
                        else
                        {
                            //如果DB和Redis不可用，返回内存缓存
                            if (isFallback)
                            {
                                return firstMetaData;
                            }
                            else
                            {
                                //Redis和DB中数据模型被删除了, 同步删除本地缓存
                                firstMetaData = GetSyncFirstMetaData(metaDataCode, metaTypeID, firstMetaData, secondModel);
                            }
                        }
                    }
                }

                //LogUtil.Log($"End GetMetaDataByCode:{metaDataCode}");
                //return firstMetaData;
                return ReWraperMetadata(firstMetaData);
            }
            catch (Exception e)
            {
                throw new TException("FAS-Meta-1002", $"获取元数据({metaDataCode})失败！", e);
            }
        }

        private IMetaData GetSyncFirstMetaData(string metaDataID, IMetaData firstModel, IMetaData secondModel)
        {
            if (secondModel != null)
            {
                firstCache.Set(secondModel);
            }
            else
            {
                firstCache.Remove(metaDataID);
            }

            firstModel = secondModel;

            return firstModel;
        }

        /// <summary>
        /// 查询元数据并同步二级缓存
        /// </summary>
        /// <param name="metaDataID">元数据ID</param>
        /// <param name="isSuccess">是否查询成功</param>
        /// <returns>元数据</returns>
        private IMetaData GetSyncSecondMetaData(string metaDataID, out bool isSuccess)
        {
            IMetaData secondMetaData = null;
            try
            {
                isSuccess = false;
                secondMetaData = secondCache.TryGet(metaDataID, out isSuccess);

                //Redis二级缓存没有或者Redis不可用时，访问数据库
                if (secondMetaData == null)
                {
                    var dbModel = new MeteDataService().GetMetaData(metaDataID);

                    if (dbModel != null)
                    {
                        secondCache.Set(dbModel);
                        secondMetaData = secondCache.TryGet(metaDataID, out isSuccess);
                    }
                    else
                    {
                        secondCache.Remove(metaDataID);
                    }
                }
                else
                {
                    //TODO 二级缓存和DB一致性控制。 例如网络抖动导致Redis更新不成功,建议后续通过补偿更新同步
                }
            }
            catch (Exception ex)
            {
                LogUtil.Log(ex.ToString());
                isSuccess = false;
                throw new TException("FAS-Meta-1001", $"从DB中获取元数据({metaDataID})失败！{ex}", ex);
            }

            return secondMetaData;
        }

        public bool IsCacheChanged(string metaDataID)
        {
            try
            {
                //var firstVersion = firstCache.GetLatestVersion(metaDataID);
                //var secondVersion = secondCache.GetLatestVersion(metaDataID);

                //return firstVersion != secondVersion;
                var firstTimeStamp = firstCache.GetTimeStamp(metaDataID);
                var secondTimeStamp = secondCache.GetTimeStamp(metaDataID);
                return firstTimeStamp != secondTimeStamp;
            }
            catch (Exception ex)
            {
                //容错降级返回一致
                return false;
            }
        }

        public void UpdateSecondCache(IMetaData domainModel, out bool isSuccess)
        {
            try
            {
                if (domainModel != null)
                {
                    secondCache.Set(domainModel);
                }

                isSuccess = true;
            }
            catch (Exception ex)
            {
                isSuccess = false;
            }
        }

        public bool TryDeleteCache(string metaDataID)
        {
            if (string.IsNullOrEmpty(metaDataID)) return true;

            try
            {
                secondCache.Remove(metaDataID);
                firstCache.Remove(metaDataID);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        #region

        private IMetaData GetSyncFirstMetaData(string metaDataCode, string metaTypeID, IMetaData firstModel, IMetaData secondModel)
        {
            if (secondModel != null)
            {
                firstCache.Set(secondModel);
            }
            else
            {
                firstCache.Remove(metaDataCode, metaTypeID);
            }

            firstModel = secondModel;

            return firstModel;
        }

        /// <summary>
        /// 查询元数据并同步二级缓存
        /// </summary>
        /// <param name="metaDataID">元数据ID</param>
        /// <param name="isSuccess">是否查询成功</param>
        /// <returns>元数据</returns>
        private IMetaData GetSyncSecondMetaData(string metaDataCode, string metaTypeID, out bool isSuccess)
        {
            IMetaData secondMetaData = null;
            try
            {
                isSuccess = false;
                secondMetaData = secondCache.TryGet(metaDataCode, metaTypeID, out isSuccess);

                //Redis二级缓存没有或者Redis不可用时，访问数据库
                if (secondMetaData == null)
                {
                    var dbModel = new MeteDataService().GetMetaDataByCode(metaDataCode, metaTypeID);

                    if (dbModel != null)
                    {
                        secondCache.Set(dbModel);
                        secondMetaData = secondCache.TryGet(metaDataCode, metaTypeID, out isSuccess);
                    }
                    else
                    {
                        secondCache.Remove(metaDataCode, metaTypeID);
                    }
                }
                else
                {
                    //TODO 二级缓存和DB一致性控制。 例如网络抖动导致Redis更新不成功,建议后续通过补偿更新同步
                }
            }
            catch (Exception ex)
            {
                LogUtil.Log(ex.ToString());
                isSuccess = false;
                throw new TException("FAS-Meta-1001", $"从DB中获取元数据({metaDataCode})失败！", ex);
            }

            return secondMetaData;
        }
        #endregion
    }
}
